package com.han.fu.api.business.service.impl;

import com.alibaba.fastjson.JSON;
import com.han.fu.api.business.entity.User;
import com.han.fu.api.business.enums.RoleEnum;
import com.han.fu.api.business.enums.SourceEnum;
import com.han.fu.api.business.model.ao.BindingAo;
import com.han.fu.api.business.model.ao.LoginAo;
import com.han.fu.api.business.model.ao.SecretAo;
import com.han.fu.api.business.model.vo.CurrentUserVo;
import com.han.fu.api.business.model.vo.LoginVo;
import com.han.fu.api.business.service.IAuthService;
import com.han.fu.api.business.service.IChannelService;
import com.han.fu.api.business.service.IUserService;
import com.han.fu.api.common.exception.BusinessException;
import com.han.fu.api.common.model.UserDetail;
import com.han.fu.api.common.utils.CurrentUserUtil;
import com.han.fu.common.constants.RedisKeyConstants;
import com.han.fu.common.constants.StringConstants;
import com.han.fu.common.utils.DateUtil;
import com.han.fu.common.utils.IdUtil;
import com.han.fu.common.utils.UuidUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author star
 * @version 1.0.0
 * @Class: AuthServiceImpl.java
 * @Description: AuthServiceImpl.java
 * @date 创建时间：2019年8月12日 下午5:36:15
 */
@Slf4j
@Service
public class AuthServiceImpl implements IAuthService {

    @Autowired
    AuthenticationManager authenticationManager;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    IUserService userService;
    @Autowired
    IChannelService channelService;
    @Autowired
    CurrentUserUtil currentUserUtil;

    private static final Long TIME_OUT = 30L;

    /**
     * 密钥认证
     *
     * @param ao 密钥数据
     * @return LoginVo 认证数据
     */
    @Override
    public LoginVo secret(SecretAo ao) {
        boolean isExist = channelService.queryChannelExist(ao.getAppId(), ao.getSecretKey());
        if (isExist) {
            User user = userService.queryUserByUnionId(ao.getUnionId());
            if (Objects.isNull(user)) {
                user = saveUser(ao);
            }
            LoginAo loginAo = new LoginAo();
            loginAo.setAccount(user.getAccount());
            try {
                Base64.Encoder encoder = Base64.getEncoder();
                loginAo.setPassword(encoder.encodeToString(user.getPassword().getBytes()));
            } catch (Exception e) {
                log.error("密码编码异常:", e);
            }
            return login(loginAo);
        } else {
            log.warn("渠道不存在：{}", JSON.toJSONString(ao));
        }
        return new LoginVo();
    }

    /**
     * 保存用户
     *
     * @param ao
     * @return
     */
    private User saveUser(SecretAo ao) {
        User user = new User();
        user.setId(IdUtil.generate());
        user.setUnionId(ao.getUnionId());
        user.setRole(RoleEnum.TOURIST.getCode());
        user.setAccount(ao.getUnionId());
        user.setPassword(UuidUtil.generateUuid());
        user.setName(ao.getName());
        user.setHeaderName(ao.getHeaderName());
        user.setSource(SourceEnum.WE_CHAT.getCode());
        user.setCreateUser(StringConstants.SYSTEM_USER);
        user.setCreateUserName(StringConstants.SYSTEM_USER_NAME);
        userService.save(user);
        return user;
    }

    /**
     * 登陆认证
     *
     * @param ao 登陆数据
     * @return LoginVo 认证数据
     */
    @Override
    public LoginVo login(LoginAo ao) {
        LoginVo vo = new LoginVo();
        try {
            // 解码
            Base64.Decoder decoder = Base64.getDecoder();
            String password = new String(decoder.decode(ao.getPassword()), "UTF-8");
            //用户验证
            final Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(ao.getAccount(), password));
            //存储认证信息
            SecurityContextHolder.getContext().setAuthentication(authentication);
            UserDetail userDetail = (UserDetail) authentication.getPrincipal();
            //生成token
            String token = UuidUtil.generateUuid();
            //存储token
            ValueOperations<String, UserDetail> valueOperations = redisTemplate.opsForValue();
            valueOperations.set(RedisKeyConstants.AUTH_TOKEN.concat(token), userDetail, TIME_OUT, TimeUnit.MINUTES);
            vo.setRole(userDetail.getRole());
            vo.setToken(token);
            vo.setExpire(DateUtil.diffDate(userDetail.getExpire()));
        } catch (DisabledException | BadCredentialsException e) {
            log.error("登陆认证失败：", e);
            throw new BusinessException("账户或密码错误");
        } catch (CredentialsExpiredException e) {
            log.error("登陆认证失败，账号已过期：", e);
            throw new BusinessException("账号已过期");
        } catch (Exception e) {
            log.error("登陆认证失败：", e);
            throw new BusinessException("账户或密码错误");
        }
        return vo;
    }

    /**
     * 退出登陆
     */
    @Override
    public void logout() {
        String token = currentUserUtil.getToken();
        // 退出登陆
        redisTemplate.delete(RedisKeyConstants.AUTH_TOKEN.concat(token));
    }

    /**
     * 获取当前登陆用户
     *
     * @return CurrentUserVo 当前用户数据
     */
    @Override
    public CurrentUserVo currentUser() {
        UserDetail userDetail = currentUserUtil.getUser();
        CurrentUserVo userVo = new CurrentUserVo();
        User user = userService.getById(userDetail.getId());
        if (null != user) {
            BeanUtils.copyProperties(user, userVo);
            Base64.Encoder encoder = Base64.getEncoder();
            try {
                userVo.setPassword(encoder.encodeToString(user.getPassword().getBytes("UTF-8")));
            } catch (UnsupportedEncodingException e) {
                log.error("密码编码失败：", e);
            }
        }
        return userVo;
    }

    /**
     * 用户绑定
     *
     * @param ao 数据
     */
    @Override
    public LoginVo bindingUser(BindingAo ao) {
        LoginAo login = new LoginAo();
        login.setPassword(ao.getPassword());
        login.setAccount(ao.getAccount());
        LoginVo vo = login(login);
        userService.bindingUser(ao);
        return vo;
    }
}


